from sqlalchemy.orm import Session
from core.response import resp_500
from core.logger import logger
from app.monitor_metrics.models.prom_alarm_strategy import PromAlarmStrategy
from app.monitor_metrics.models.prom_alarm_template import PromAlarmTemplate
from app.monitor_metrics.models.prom_alarm_metrics import PromAlarmMetrics
from app.monitor_metrics.models.prom_host_group import PromHostGroup
from app.monitor_metrics.models.prom_alarm_group import PromAlarmGroup
from app.monitor_metrics.models.prom_alarm_rule import PromAlarmRule
from app.monitor_metrics.handlers.prom_alarm_group_handler import PromAlarmGroupHandler
from app.monitor_metrics.handlers.prom_alarm_rule_handler import PromAlarmRuleHandler
from app.monitor_metrics.utils  import PromCommonUtils

class PromAlarmStrategyHandler():
    """  PromAlarmStrategy Handler
    """
    def __init__(self):
        self.prom_alarm_group_handler = PromAlarmGroupHandler()
        self.prom_alarm_rule_handler = PromAlarmRuleHandler()


    def get_alarm_strategy_search(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段模糊匹配, 获取告警策略信息
        """
        try:
            field_info = {field: value}
            order_info = {"classes": "asc", "name": "asc"}
            result = PromAlarmStrategy.filter_by_field_search(db, page, per_page, field_info, order_info)
            if not result:
                logger.warning(f"get alarm strategy search, result is null => {field}:{value}")
                return False, None

        except Exception as e:
            message = f"get alarm strategy search by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True, result



    def get_alarm_strategy_match(self, db: Session, page: int, per_page: int, field: str, value: str):
        """
        根据表字段完全匹配, 获取告警策略信息
        """
        try:
            result = PromAlarmStrategy.filter_by_field_match(db, page, per_page, {field: value})
            if not result:
                logger.warning(f"get alarm strategy match, result is null => {field}:{value}")
                return False

        except Exception as e:
            message = f"get alarm strategy match by {field}:{value} error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return result



    def add_alarm_strategy(self, db: Session, data: dict):
        """
        创建告警策略
        """
        try:
            # 检查是否已经存
            alarm_strategy_object = PromAlarmStrategy.get_object_by_name(db, data["name"])
            if alarm_strategy_object:
                # 如果存在， 则异常返回
                logger.error(f"add alarm strategy failed, name:{data['name']} was exist")
                return False

            # 检查告警指标是否存在
            alarm_metrics_object = PromAlarmMetrics.get_object_by_uuid(db, data["alarm_metrics_uuid"])
            if not alarm_metrics_object:
                # 如果不存在， 则异常返回
                logger.error(f"add alarm strategy failed, alarm_metrics_uuid:{data['alarm_metrics_uuid']} was not exist")
                return False

            # 检查告警模板是否存在
            alarm_template_object = PromAlarmTemplate.get_object_by_uuid(db, data["template_uuid"])
            if not alarm_template_object:
                # 如果不存在， 则异常返回
                logger.error(f"add alarm strategy failed, template_uuid:{data['template_uuid']} was not exist")
                return False

            # 添加到数据库
            data["classes"] = alarm_template_object.classes
            result = PromAlarmStrategy.add(db, data)
            if not result:
                logger.error(f"add alarm strategy to database failed")
                return False

            # 设置告警模板更新标签
            if not alarm_template_object.is_update:
                result = PromAlarmTemplate.set_is_update(db, alarm_template_object.uuid, True)
                if not result:
                    logger.error(f"add_alarm_strategy set template:is_update to database failed")
                    return False
        except Exception as e:
            message = f"add alarm strategy error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True




    def update_alarm_strategy(self, db: Session, data: dict):
        """
        修改告警策略信息
        """
        try:
            alarm_strategy_object = PromAlarmStrategy.get_object_by_uuid(db, data["uuid"])
            if not alarm_strategy_object:
                # 如果不存在， 则异常返回
                logger.error(f"update alarm strategy failed, uuid:{data['uuid']} not exist")
                return False

            # 检查告警指标是否存在
            alarm_metrics_object = PromAlarmMetrics.get_object_by_uuid(db, data["alarm_metrics_uuid"])
            if not alarm_metrics_object:
                # 如果不存在， 则异常返回
                logger.error(f"update alarm strategy failed, alarm_metrics_uuid:{data['alarm_metrics_uuid']} was not exist")
                return False

            # 更新告警策略到数据库
            result = PromAlarmStrategy.update(db, data)
            if not result:
                logger.error(f"update alarm strategy to database failed")
                return False

            # 更新告警模板的同步标签
            strategy_info = PromAlarmStrategy.get_object_info_by_uuid(db, data["uuid"])
            result = PromAlarmTemplate.set_is_update(db, strategy_info["template_uuid"], True)
            if not result:
                logger.error(f"update_alarm_strategy set template:is_update to database failed")
                return False
        except Exception as e:
            message = f"update alarm strategy error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True



    def delete_alarm_strategy(self, db: Session, data: dict):
        """
        删除告警策略信息
        """
        try:
            alarm_strategy_object = PromAlarmStrategy.get_object_by_uuid(db, data["uuid"])
            if not alarm_strategy_object:
                # 如果不存在， 则异常返回
                logger.error(f"delete alarm strategy failed, uuid:{data['uuid']} not exist")
                return False

            # 删除告警策略
            PromAlarmStrategy.delete_object_by_uuid(db, data['uuid'])

            # 更新告警模板的同步标签
            result = PromAlarmTemplate.set_is_update(db, alarm_strategy_object.template_uuid, True)
            if not result:
                logger.error(f"delete_alarm_strategy set template:is_update to database failed")
                return False
        except Exception as e:
            message = f"delete alarm strategy error"
            logger.exception(f"{message} => {e}")
            return resp_500(message)

        return True





    #### 主机组组绑定模板，根据告警策略生成告警规则 ####
    def host_convert_alarm_rule_data(self, db: Session, host_info: dict, template_info: dict, strategy_info: dict):
        """
        根据模板/策略的信息，转换成创建告警规则的信息
        """
        #获取基础信息
        host_group_info = PromHostGroup.get_object_info_by_uuid(db, host_info["host_group_uuid"])

        # 准备信息
        strategy_info["alarm_metrics_parm"]["i_host_name="] = host_info['name']
        strategy_info['labels']['i_host_name'] = host_info['name']
        strategy_info['labels']['i_host_group'] = host_group_info["name"]
        strategy_info['labels']['i_department'] = host_group_info["department"]

        alarm_group_name = PromCommonUtils.get_alarm_group_name(host_info, template_info)
        alarm_group_uuid = PromAlarmGroup.get_object_by_name(db, alarm_group_name).uuid
        alarm_rule_name = PromCommonUtils.get_alarm_rule_name(host_info, template_info, strategy_info)

        # 组装信息
        rule_data = {
            "name": alarm_rule_name,
            "alarm_group_uuid": alarm_group_uuid,
            "type": "alarm",
            "state": strategy_info['state'],
            "custom": 0,
            "target": host_info['name'],
            "alarm_metrics_uuid": strategy_info["alarm_metrics_uuid"],
            "alarm_metrics_parm": strategy_info["alarm_metrics_parm"],
            "operator": strategy_info['operator'],
            "value": strategy_info['value'],
            "duration": strategy_info['duration'],
            "notice_teams_uuid": host_group_info['notice_teams_uuid'],
            "notice_count": strategy_info['notice_count'],
            "level": strategy_info['level'],
            "maintain_begin": strategy_info['maintain_begin'] if "maintain_begin" in strategy_info else "",
            "maintain_end": strategy_info['maintain_end'] if "maintain_end" in strategy_info else "",
            "maintain_repeat": strategy_info['maintain_repeat'],
            "callback": strategy_info['callback'],
            "labels": strategy_info['labels'],
            "annotations": strategy_info['annotations'],
            "comment": f"告警模板:{template_info['name']} 绑定 主机组:{host_group_info['name']} 里面的告警策略:{strategy_info['name']} 生成的告警规则"
        }

        # 如果告警规则已存在，则添加uuid，即用作更新
        alarm_rule_object = PromAlarmRule.get_object_by_name(db, alarm_rule_name)
        if alarm_rule_object:
            rule_data["uuid"] = alarm_rule_object.uuid

        return rule_data


    def host_check_alarm_strategy_priority(self, db: Session, host_info: dict, template_info: dict, strategy_info: dict):
        """
        检查主机告警策略是否能创建告警规则
        """
        # 从主机组继承的模板告警策略
        if template_info["type"] == "bind":
            host_template_info = PromAlarmTemplate.get_object_info_by_uuid(db, host_info["template_uuid"])
            for host_strategy_info in host_template_info["strategies"]:
                # 遍历所有的自定义告警策略，如果有冲突，则不能创建
                if host_strategy_info["priority"] and host_strategy_info["alarm_metrics_uuid"] ==  strategy_info["alarm_metrics_uuid"]:
                    return False
            # 没有冲突才能创建
            return True

        # 主机自定义的告警策略:type=="auto". 现版本就两种类型，暂时不需要加判断
        # 遍历主机所有的继承模板告警策略
        host_group_info = PromHostGroup.get_object_info_by_uuid(db, host_info["host_group_uuid"])
        for alarm_template_uuid in host_group_info["templates_uuid"]:
            alarm_template_info = PromAlarmTemplate.get_object_info_by_uuid(db, alarm_template_uuid)
            for alarm_strategy_info in alarm_template_info["strategies"]:
                # 如果有冲突
                if alarm_strategy_info["alarm_metrics_uuid"] == strategy_info["alarm_metrics_uuid"]:
                    if strategy_info["priority"]:
                        # 优先告警策略
                        # 需要删除冲突的告警策略
                        self.host_strategy_delete_alarm_rule(db, host_info, alarm_template_info, alarm_strategy_info)
                    else:
                        # 共享告警策略
                        # 添加冲突的告警策略
                        self.host_create_alarm_rule(db, host_info, alarm_template_info, alarm_strategy_info)
        # 自定义的告警策略，一定会创建
        return True


    def host_create_alarm_rule(self, db: Session, host_info: dict, template_info: dict, strategy_info: dict):
        # 根据告警策略，创建对应的告警规则
        rule_name = PromCommonUtils.get_alarm_rule_name(host_info, template_info, strategy_info)
        try:
            if PromAlarmRule.get_object_by_name(db, rule_name):
                logger.warning(f"create alarm rule nothing, rule:{rule_name} was exist")
                return True

            rule_data = self.host_convert_alarm_rule_data(db, host_info, template_info, strategy_info)
            result = self.prom_alarm_rule_handler.add_alarm_rule_to_database(db, rule_data)
            if not result:
                logger.error(f"create alarm rule:{rule_name} failed from strategy")
                return False
        except Exception as e:
            logger.exception(f"create alarm rule:{rule_name} error from strategy => {e}")
            return False

        logger.info(f"create alarm rule:{rule_name} success from strategy")
        return True


    def host_strategy_delete_alarm_rule(self, db: Session, host_info: dict, template_info: dict, strategy_info: dict):
        # 根据告警策略，删除对应的告警规则
        rule_name = PromCommonUtils.get_alarm_rule_name(host_info, template_info, strategy_info)
        rule_object = PromAlarmRule.get_object_by_name(db, rule_name)
        if rule_object:
            PromAlarmRule.delete_object_by_uuid(db, rule_object.uuid)
            logger.info(f"delete alarm rule{rule_name} success")

        return True


    def host_strategy_to_alarm_rule(self, db: Session, host_info: dict, template_info: dict, strategy_info: dict):
        """
        主机，根据告警策略，创建对应的告警规则
        """
        # 检查优先级是否有冲突
        result = self.host_check_alarm_strategy_priority(db, host_info, template_info, strategy_info)
        if not result:
            logger.warning(f"host_strategy check alarm strategy priority:{strategy_info['name']} failed")
            return True

        # 同步告警策略到告警规则
        result = self.host_create_alarm_rule(db, host_info, template_info, strategy_info)
        if not result:
            logger.error(f"host_strategy create alarm rule:{strategy_info['name']} failed")
            return False

        return True





    #### 网络探测组绑定模板时，根据告警策略生成告警规则 ####
    def network_group_strategy_to_alarm_rule_data(self, db: Session, network_group_info: dict, template_info: dict, strategy_info: dict):
        """
        根据模板/策略的信息，转换成创建告警规则的信息
        """
        # 准备信息
        strategy_info["alarm_metrics_parm"]["i_network_group="] = network_group_info["name"]
        strategy_info['labels']['i_department'] = network_group_info["department"]
        strategy_info['labels']['i_network_group'] = network_group_info["name"]

        alarm_group_name = PromCommonUtils.get_alarm_group_name(network_group_info, template_info)
        alarm_group_uuid = PromAlarmGroup.get_object_by_name(db, alarm_group_name).uuid
        alarm_rule_name = PromCommonUtils.get_alarm_rule_name(network_group_info, template_info, strategy_info)

        # 组装信息
        rule_data = {
            "name": alarm_rule_name,
            "alarm_group_uuid": alarm_group_uuid,
            "type": "alarm",
            "state": strategy_info['state'],
            "custom": 0,
            "target": network_group_info['name'],
            "alarm_metrics_uuid": strategy_info["alarm_metrics_uuid"],
            "alarm_metrics_parm": strategy_info["alarm_metrics_parm"],
            "operator": strategy_info['operator'],
            "value": strategy_info['value'],
            "duration": strategy_info['duration'],
            "notice_teams_uuid": network_group_info['notice_teams_uuid'],
            "notice_count": strategy_info['notice_count'],
            "level": strategy_info['level'],
            "maintain_begin": strategy_info['maintain_begin'] if "maintain_begin" in strategy_info else "",
            "maintain_end": strategy_info['maintain_end'] if "maintain_end" in strategy_info else "",
            "maintain_repeat": strategy_info['maintain_repeat'],
            "callback": strategy_info['callback'],
            "labels": strategy_info['labels'],
            "annotations": strategy_info['annotations'],
            "comment": f"网络探测组:{network_group_info['name']} 绑定 告警模板:{template_info['name']} 告警策略:{strategy_info['name']} 生成的告警规则"
        }

        # 如果告警规则已存在，则添加uuid，即用作更新
        alarm_rule_object = PromAlarmRule.get_object_by_name(db, alarm_rule_name)
        if alarm_rule_object:
            rule_data["uuid"] = alarm_rule_object.uuid

        return rule_data


    def network_group_strategy_to_alarm_rule(self, db: Session, network_group_info: dict, template_info: dict, strategy_info: dict):
        # 网络探测组，根据告警策略，创建对应的告警规则
        rule_name = PromCommonUtils.get_alarm_rule_name(network_group_info, template_info, strategy_info)
        try:
            if PromAlarmRule.get_object_by_name(db, rule_name):
                logger.warning(f"network_group_strategy_to_alarm_rule nothing, rule:{rule_name} was exist")
                return True

            # 生成告警规则的数据结构，创建告警规则
            rule_data = self.network_group_strategy_to_alarm_rule_data(db, network_group_info, template_info, strategy_info)
            result = self.prom_alarm_rule_handler.add_alarm_rule_to_database(db, rule_data)
            if not result:
                logger.error(f"network_group_strategy_to_alarm_rule: create alarm rule:{rule_name} failed")
                return False
        except Exception as e:
            logger.exception(f"network_group_strategy_to_alarm_rule: create alarm rule:{rule_name} error => {e}")
            return False

        logger.info(f"network_group_strategy_to_alarm_rule: create alarm rule:{rule_name} success")
        return True
